package me.zhengjie.modules.security.config

//import org.springframework.security.config.Customizer

import jakarta.servlet.DispatcherType
import me.zhengjie.common.annotation.AnonymousAccess
import me.zhengjie.common.utils.enums.RequestMethodEnum
import me.zhengjie.logging.slf4j.logInfo
import me.zhengjie.modules.security.config.bean.SecurityProperties
import me.zhengjie.modules.security.security.*
import me.zhengjie.modules.security.service.OnlineUserService
import me.zhengjie.modules.security.service.UserCacheManager
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.annotation.Order
import org.springframework.http.HttpMethod
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.ProviderManager
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configurers.*
import org.springframework.security.config.core.GrantedAuthorityDefaults
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.web.filter.CorsFilter
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.mvc.method.RequestMappingInfo
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
import java.util.*


@Configuration
@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true) //public class SecurityConfig extends WebSecurityConfigurerAdapter {
class SecurityConfig(
    //private val jwtUserDetailsService: JwtUserDetailsService, // 自定义基于JWT的安全过滤器
    private val userDetailsService: UserDetailsService, // 自定义基于JWT的安全过滤器
    private val tokenProvider: TokenProvider,
    private val corsFilter: CorsFilter,
    private val authenticationErrorHandler: JwtAuthenticationEntryPoint,
    private val jwtAccessDeniedHandler: JwtAccessDeniedHandler,
    private val properties: SecurityProperties,
    private val onlineUserService: OnlineUserService,
    private val userCacheManager: UserCacheManager,
    private val applicationContext: ApplicationContext
) {
    //@Value("\${jwt.header}")
    //private val tokenHeader: String? = null

    @Bean
    fun grantedAuthorityDefaults(): GrantedAuthorityDefaults {
        // Remove the ROLE_ prefix
        return GrantedAuthorityDefaults("")
    }

    @Bean
    fun passwordEncoderBean(): PasswordEncoder {
        return BCryptPasswordEncoder()
    }

    private fun securityConfigurerAdapter(): TokenConfigurer {
        return TokenConfigurer(tokenProvider, properties, onlineUserService, userCacheManager)
    }

    @Bean
    @Order(1)
    @Throws(Exception::class)
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        // 搜寻匿名标记 url： @AnonymousAccess
        val requestMappingHandlerMapping = applicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping::class.java)
        val handlerMethodMap = requestMappingHandlerMapping.handlerMethods
        // 获取匿名标记
        val anonymousUrls = getAnonymousUrl(handlerMethodMap)

        http
            //.formLogin { form: FormLoginConfigurer<HttpSecurity?> -> form.loginPage("/tlogin.html").permitAll() }
            .headers {it.frameOptions { it.disable() }}
            .csrf { csrf: CsrfConfigurer<HttpSecurity> -> csrf.disable() } //.authorizeRequests((authorize) -> authorize
            .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter::class.java)

            //.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter::class.java)

            .authorizeHttpRequests{ authorize ->
                authorize
                        .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 首页和登录页面 不需要认证
                        .requestMatchers(
                            "/",
                            "/auth/login",
                            "/auth/code",
                            "/auth/smsCode",
                            "/auth/register",
                            "/auth/resetPass",
                            "/auth/phoneResetPass"
                        ).permitAll() // 静态资源，可自行修改
                        //springboot 3以上版本，不能两级**路径
//                        .requestMatchers(
//                            HttpMethod.GET,
//                            "/*.html",
//                            "/**/*.html",
//                            "/**/*.css",
//                            "/**/*.js"
//                        ).anonymous()
                    //配置完静态资源路径授权后，需要到ConfigurerAdapter中配置静态资源路径映射
                        .requestMatchers(
                            HttpMethod.GET,
                            "/**.html",
                            "/**.html",
                            "/**.css",
                            "/**.js",
                            "/profile/**",
                            "/resource/**",
                            "/static/**",
                            "/webSocket/**",
                            //"/templates/**",
                            "/web/**"
                        ).permitAll()
                        .requestMatchers("/public/**").permitAll()
                        .requestMatchers("/public/file/**").permitAll()
                        .requestMatchers("/public/uploadfiles/**").permitAll()
                        //.requestMatchers("/swagger-ui.html").permitAll()
                        //.requestMatchers("/swagger-resources/**").permitAll()
                        //.requestMatchers("/v2/api-docs").permitAll()
                        //.requestMatchers("/webjars/**").permitAll()
                        //.requestMatchers("/**/api-docs").permitAll() // swagger end
                        //springdoc
                        .requestMatchers("/swagger-ui/**").permitAll()
                        .requestMatchers("/v3/api-docs").permitAll()

                        // 文件
                        .requestMatchers("/avatar/**").permitAll()
                        .requestMatchers("/file/**").permitAll() // 放行OPTIONS请求
                        .requestMatchers("/druid/**").permitAll() // 自定义匿名访问所有url放行 ： 允许 匿名和带权限以及登录用户访问

                        // 自定义匿名访问所有url放行：允许匿名和带Token访问，细腻化到每个 Request 类型
                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                        .requestMatchers(HttpMethod.GET, *anonymousUrls[RequestMethodEnum.GET.type]?.toTypedArray() ?: arrayOf()).permitAll()
                        .requestMatchers(HttpMethod.POST, *anonymousUrls[RequestMethodEnum.POST.type]?.toTypedArray() ?: arrayOf()).permitAll()
                        .requestMatchers(HttpMethod.PUT, *anonymousUrls[RequestMethodEnum.PUT.type]?.toTypedArray() ?: arrayOf()).permitAll()
                        .requestMatchers(HttpMethod.PATCH, *anonymousUrls[RequestMethodEnum.PATCH.type]?.toTypedArray() ?: arrayOf()).permitAll()
                        .requestMatchers(HttpMethod.DELETE, *anonymousUrls[RequestMethodEnum.DELETE.type]?.toTypedArray() ?: arrayOf()).permitAll()
                        // 所有类型的接口都放行
                        .requestMatchers(*anonymousUrls[RequestMethodEnum.ALL.type]?.toTypedArray() ?: arrayOf()).permitAll()
                        // 所有请求都需要认证
                        .anyRequest().authenticated()
                }
            .exceptionHandling { exception: ExceptionHandlingConfigurer<HttpSecurity?> ->
                exception
                    .authenticationEntryPoint(authenticationErrorHandler)
                    .accessDeniedHandler(jwtAccessDeniedHandler)
            }
            .sessionManagement { session: SessionManagementConfigurer<HttpSecurity?> ->
                session
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            } // 禁用会话管理

        return http.build().apply { securityConfigurerAdapter() }
    }

    @Bean
    @Throws(Exception::class)
    fun authenticationManager(
        //authenticationConfiguration: AuthenticationConfiguration
    ): AuthenticationManager {
        //authenticationConfiguration.authenticationManagerBuilder()
        val authenticationProvider = DaoAuthenticationProvider()
            authenticationProvider.setUserDetailsService(userDetailsService)
            authenticationProvider.setPasswordEncoder(passwordEncoderBean())
        //return authenticationConfiguration.authenticationManager
        return ProviderManager(authenticationProvider)
    }

    private fun getAnonymousUrl(handlerMethodMap: Map<RequestMappingInfo, HandlerMethod>): Map<String, Set<String>> {
        val anonymousUrls = HashMap<String, HashSet<String>>(8)
        val get = HashSet<String>()
        val post = HashSet<String>()
        val put = HashSet<String>()
        val patch = HashSet<String>()
        val delete = HashSet<String>()
        val all = HashSet<String>()

        for ((key, value) in handlerMethodMap) {
            val handlerMethod = value
            val anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess::class.java)
            if (anonymousAccess != null) {
                val requestMethods = key.methodsCondition.methods.toList()
                val request = RequestMethodEnum.find(
                    if (requestMethods.isEmpty()) RequestMethodEnum.ALL.type else requestMethods[0].name
                )
                val patterns = key.patternsCondition?.patterns.orEmpty() + key.pathPatternsCondition?.patterns.orEmpty()
                when (request) {
                    RequestMethodEnum.GET -> get.addAll(patterns as Collection<String>)
                    RequestMethodEnum.POST -> post.addAll(patterns as Collection<String>)
                    RequestMethodEnum.PUT -> put.addAll(patterns as Collection<String>)
                    RequestMethodEnum.PATCH -> patch.addAll(patterns as Collection<String>)
                    RequestMethodEnum.DELETE -> delete.addAll(patterns as Collection<String>)
                    else -> all.addAll(patterns as Collection<String>)
                }
            }else{
                //logInfo("Method ${handlerMethod.method.name} does not have the @AnonymousAccess annotation")
            }
        }

        anonymousUrls[RequestMethodEnum.GET.key] = get
        anonymousUrls[RequestMethodEnum.POST.key] = post
        anonymousUrls[RequestMethodEnum.PUT.key] = put
        anonymousUrls[RequestMethodEnum.PATCH.key] = patch
        anonymousUrls[RequestMethodEnum.DELETE.key] = delete
        anonymousUrls[RequestMethodEnum.ALL.key] = all

        return anonymousUrls
    }
}